//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.RegistryManagerModule
{
    using System.ComponentModel;
    using System.Text;
    using System.Windows;
    using ManagementStudioServices.Interfaces;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities.Properties;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Practices.Unity;
    using SR = Microsoft.Research.DataLayer;
    using Microsoft.Research.Authentication;
    using System.Collections.ObjectModel;
    using System;
    using System.Linq;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using System.Globalization;

    /// <summary>
    /// Workflow Catalog CenterPane Presenter
    /// </summary>
    public class GroupCenterPanePresenter : IRegistryManagerCenterPanePresenter, INotifyPropertyChanged
    {
        private GroupViewModel selectedModel;

        private GroupViewModel editableGroupModel;

        private IUnityContainer container;

        private IRegistryManagerService registryManagerService;

        private bool saveInProgress;

        /// <summary>
        /// Gets or sets the UpdateCommand command.
        /// </summary>
        /// <value>The browse category command.</value>
        public DelegateCommand<BaseRegMgrModel> UpdateCommand { get; set; }

        /// <summary>
        /// Gets or sets the save category changes command.
        /// </summary>
        /// <value>The save category changes command.</value>
        public DelegateCommand<object[]> SaveGroupCommand { get; set; }

        /// <summary>
        /// Gets or sets the select root command.
        /// </summary>
        /// <value>The select root command.</value>
        public DelegateCommand<object> SelectRootCommand { get; set; }

        /// <summary>
        /// Gets or sets the cancel category changes command.
        /// </summary>
        /// <value>The cancel category changes command.</value>
        public DelegateCommand<object> CancelUserChangesCommand { get; set; }


        /// <summary>
        /// Gets or sets the delete category command.
        /// </summary>
        /// <value>The delete category command.</value>
        public DelegateCommand<object> DeleteGroupCommand { get; set; }

        /// <summary>
        /// Gets or sets the Add New Group command.
        /// </summary>
        /// <value>The adds category command.</value>
        public DelegateCommand<object> AddNewGroupCommand { get; set; }

        /// <summary>
        /// Gets or sets the delete category command.
        /// </summary>
        /// <value>The delete category command.</value>
        public DelegateCommand<BaseRegMgrModel> DeleteCommand { get; set; }


        /// <summary>
        /// Gets or sets the Add Group Credential command.
        /// </summary>
        /// <value>The adds Group command.</value>
        public DelegateCommand<object> AddGroupCredentialCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove group category command.
        /// </summary>
        /// <value> Remove category command.</value>
        public DelegateCommand<object> RemoveGroupCredentialCommand { get; set; }

        /// <summary>
        /// Gets or sets the Add User Credential command.
        /// </summary>
        /// <value>The adds Group command.</value>
        public DelegateCommand<object> AddUserCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove User category command.
        /// </summary>
        /// <value> Remove category command.</value>
        public DelegateCommand<object> RemoveUserCommand { get; set; }


        /// <summary>
        /// Selected Credentilas from the Crdentila list box
        /// </summary>
        public LogOnCredentialModel SelectedCredentials { get; set; }

        /// <summary>
        /// Selected Credentilas from the Crdentila list box
        /// </summary>
        public SearchUserModel SelectedUser { get; set; }

        public GroupCenterPanePresenter(IUnityContainer container, GroupCatalogCenterPane contentView, IRegistryManagerService service)
        {
            this.container = container;
            contentView.DataContext = this;
            this.ContentView = contentView;
            this.registryManagerService = service;
            this.container = container;
            this.registryManagerService = service;
            this.SaveGroupCommand = new DelegateCommand<object[]>(this.SaveUserChangesExecute);
            this.CancelUserChangesCommand = new DelegateCommand<object>(p => this.CancelUserChangesExecute());
            this.DeleteGroupCommand = new DelegateCommand<object>(p => this.DeleteGroupCommandExecute(), p => this.CanExecuteDelete());
            this.AddNewGroupCommand = new DelegateCommand<object>(p => this.AddNewGroupCommandExecute());

            this.AddGroupCredentialCommand = new DelegateCommand<object>(p => this.AddGroupCredentialCommandExecute());
            this.RemoveGroupCredentialCommand = new DelegateCommand<object>(p => this.RemoveGroupCommandExecute(), p => this.CanExecuteRemoveCredentials());

            this.AddUserCommand = new DelegateCommand<object>(p => this.AddUserCommandExecute());
            this.RemoveUserCommand = new DelegateCommand<object>(p => this.RemoveUserCommandExecute(), p => this.CanExecuteRemoveUser());

        }

        /// <summary>
        /// Determines whether this instance [can execute delete].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can execute delete]; otherwise, <c>false</c>.
        /// </returns>
        private bool CanExecuteDelete()
        {
            if (this.EditableGroupModel != null && !this.EditableGroupModel.IsBuiltin)
            {
                return this.EditableGroupModel.NonVirtualGroup;
            }

            return false;
        }

        /// <summary>
        /// Enables/Disables the Remove User button 
        /// </summary>
        /// <returns>true if there atleast one user, false otherwise</returns>
        private bool CanExecuteRemoveUser()
        {
            return this.EditableGroupModel != null && this.EditableGroupModel.Users != null && this.EditableGroupModel.Users.Count > 0;
        }

        /// <summary>
        /// Enables/Disables the Remove credential button 
        /// </summary>
        /// <returns>true if there atleast one credential, false otherwise</returns>
        private bool CanExecuteRemoveCredentials()
        {
            return this.EditableGroupModel != null && this.EditableGroupModel.Credentials != null && this.EditableGroupModel.Credentials.Count > 0;
        }

        /// <summary>
        /// Saves the changes.
        /// </summary>
        /// <returns>True if save is success.</returns>
        public SaveStateHandler SaveChanges()
        {
            return this.EnforceSaveForChanges();
        }

        #region IRegistryManagerCenterPanePresenter Members

        public UIElement ContentView
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the refresh command.
        /// </summary>
        /// <value>The refresh command.</value>
        public DelegateCommand<BaseRegMgrModel> RefreshCommand { get; set; }

        /// <summary>
        /// Gets or sets the editable category model.
        /// </summary>
        /// <value>The editable category model.</value>
        public GroupViewModel EditableGroupModel
        {
            get
            {
                return this.editableGroupModel;
            }

            set
            {
                this.editableGroupModel = value;
                this.OnNotifyPropertyChanged("EditableGroupModel");
            }
        }

        /// <summary>
        /// Gets or sets the selected model.
        /// </summary>
        /// <value>The selected model.</value>
        public BaseRegMgrModel SelectedModel
        {
            get
            {
                return this.selectedModel;
            }
            set
            {
                if (null != this.EditableGroupModel && !this.EditableGroupModel.Id.Equals(value.Id))
                {
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
                this.selectedModel = value as GroupViewModel;

                this.GetAllRoles();
                this.GetAllCredentials();
                this.GetAllUsers();
                this.EditableGroupModel = GroupViewModel.CreateClone(this.selectedModel);
                this.DeleteGroupCommand.RaiseCanExecuteChanged();
                this.RemoveGroupCredentialCommand.RaiseCanExecuteChanged();
                this.RemoveUserCommand.RaiseCanExecuteChanged();
            }
        }

        #endregion


        private void GetAllCredentials()
        {
            this.selectedModel.Credentials = new ObservableCollection<LogOnCredentialModel>();
            SR.Group group = null;
            if (!this.selectedModel.Id.Equals(Guid.Empty))
            {
                try
                {
                    group = SR.Group.Load(this.selectedModel.Id, this.registryManagerService.RegistryConnection);
                }
                catch (SR.RecordNotFoundException)
                {
                    return;
                }

                foreach (SR.ISecurityCredential credential in ((SR.ISecurityPrincipal)group).Credentials)
                {
                    LogOnCredentialModel credentialModel = new LogOnCredentialModel(credential.Credential, string.Empty);
                    this.selectedModel.Credentials.Add(credentialModel);
                }
            }
        }


        private void GetAllRoles()
        {
            this.selectedModel.Roles = new ObservableCollection<CredentialRoleModel>();
            this.GetAllDefaultRoles();
            this.GetActiveRoles();
        }


        private void GetAllDefaultRoles()
        {
            SR.SystemRoleType rolesEnums = new SR.SystemRoleType();
            string[] roleNames = Enum.GetNames(rolesEnums.GetType());
            foreach (string role in roleNames)
            {
                CredentialRoleModel roleModel = new CredentialRoleModel(role, false);
                this.selectedModel.Roles.Add(roleModel);
            }
        }


        private void GetActiveRoles()
        {
            SR.Group group = null;
            if (!this.selectedModel.Id.Equals(Guid.Empty))
            {
                try
                {
                    group = SR.Group.Load(this.selectedModel.Id, this.registryManagerService.RegistryConnection);
                }
                catch (SR.RecordNotFoundException)
                {
                    return;
                }

                foreach (SR.SystemRole role in group.SystemRoles)
                {
                    CredentialRoleModel rolemodel = this.selectedModel.Roles.FirstOrDefault(p => p.RoleName.Equals(GroupCenterPanePresenter.GetRoleName(role.Role)));
                    if (rolemodel != null)
                    {
                        rolemodel.RoleSelected = true;
                    }
                }
            }
        }

        public static string GetRoleName(SR.SystemRoleType role)
        {
            return Enum.GetName(role.GetType(), role);
        }

        private void GetAllUsers()
        {
            this.selectedModel.Users = new ObservableCollection<SearchUserModel>();
            SR.Group group = null;
            if (!this.SelectedModel.Id.Equals(Guid.Empty))
            {
                try
                {
                    group = SR.Group.Load(this.SelectedModel.Id, this.registryManagerService.RegistryConnection);
                }
                catch (SR.RecordNotFoundException)
                {
                    return;
                }
                foreach (SR.User user in group.Users)
                {
                    if (!user.IsDeleted)
                    {
                        SearchUserModel userModel = new SearchUserModel(user.Name, string.Empty, string.Empty);
                        this.selectedModel.Users.Add(userModel);
                    }
                }
            }
        }

        /// <summary>
        /// Enforces whether Save is required for the Model 
        /// </summary>
        /// <returns>SaveStateHolder</returns>
        private SaveStateHandler EnforceSaveForChanges()
        {
            SaveStateHandler saveSuccess = new SaveStateHandler(SaveStatus.Valid);
            if (!this.saveInProgress &&
                this.EditableGroupModel != null && !this.editableGroupModel.IsDeleted
                && !this.EditableGroupModel.Equals(this.SelectedModel as GroupViewModel))
            {
                MessageBoxResult userResult = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(ManagementStudioResourceManager.GetString("SaveChangesToGroup"));
                if (userResult == MessageBoxResult.Yes)
                {
                    this.SaveGroupCommand.Execute(new object[] { false, saveSuccess });
                }
                else
                {
                    this.Rollback();
                }
            }

            return saveSuccess;
        }

        /// <summary>
        /// Handler Cancel command
        /// </summary>
        private void CancelUserChangesExecute()
        {
            this.Rollback();
            if (this.SelectRootCommand != null)
            {
                this.SelectRootCommand.Execute(null);
            }
        }

        /// <summary>
        /// Roll back all changes.
        /// </summary>
        private void Rollback()
        {
            if (this.SelectedModel != null)
            {
                this.EditableGroupModel = GroupViewModel.CreateClone(this.SelectedModel as GroupViewModel);
            }
        }

        private void RemoveGroupCommandExecute()
        {
            if (this.SelectedCredentials != null)
            {
                if (this.EditableGroupModel.Credentials.Contains(this.SelectedCredentials))
                {
                    this.EditableGroupModel.Credentials.Remove(this.SelectedCredentials);
                    this.RemoveGroupCredentialCommand.RaiseCanExecuteChanged();
                }
            }
        }

        private void RemoveUserCommandExecute()
        {
            if (this.SelectedUser != null)
            {
                if (this.EditableGroupModel.Users.Contains(this.SelectedUser))
                {
                    this.EditableGroupModel.Users.Remove(this.SelectedUser);
                    this.RemoveUserCommand.RaiseCanExecuteChanged();
                }
            }

        }

        private void AddUserCommandExecute()
        {
            SearchUserPresenter searchUserPresenter = new SearchUserPresenter(this.container, this.registryManagerService);
            searchUserPresenter.ShowCommand.Execute(null);
            if (searchUserPresenter.SelectUserModel != null && searchUserPresenter.SelectUserModel.Count > 0)
            {
                StringBuilder errorString = new StringBuilder();
                foreach (SearchUserModel model in searchUserPresenter.SelectUserModel)
                {
                    SearchUserModel searchedModel = this.EditableGroupModel.Users.FirstOrDefault(p => p.UserName.Equals(model.UserName));

                    if (searchedModel == null)
                    {
                        this.EditableGroupModel.Users.Add(model);
                        this.RemoveUserCommand.RaiseCanExecuteChanged();
                    }
                    else
                    {
                        errorString.Append(model.UserName + Environment.NewLine);
                    }
                }

                if (errorString.Length > 0)
                {
                    TridentMessageBox.ShowTridentInformationMessageBox(ManagementStudioResourceManager.GetString("ErrorUserExist") + Environment.NewLine + errorString.ToString());
                }
            }

        }

        /// <summary>
        /// Handler for Add Group credential command
        /// </summary>
        private void AddGroupCredentialCommandExecute()
        {
            AddCredentialPresenter addGroupCredentials = new AddCredentialPresenter(this.container);
            addGroupCredentials.ShowCommand.Execute(CredentialType.Group);
            if (addGroupCredentials.Credential != null)
            {
                LogOnCredentialModel loginCredential = new LogOnCredentialModel(addGroupCredentials.Credential.Name, addGroupCredentials.Credential.DisplayName);

                LogOnCredentialModel searchedModel = this.EditableGroupModel.Credentials.FirstOrDefault(p => p.CredentialName.ToLower(CultureInfo.CurrentCulture).Equals(loginCredential.CredentialName.ToLower(CultureInfo.CurrentCulture)));

                if (searchedModel == null)
                {
                    this.EditableGroupModel.Credentials.Add(loginCredential);
                    this.RemoveGroupCredentialCommand.RaiseCanExecuteChanged();
                }
                else
                {
                    TridentMessageBox.ShowTridentInformationMessageBox(ManagementStudioResourceManager.GetString("ErrorCredentialExist"));
                }
            }
        }


        /// <summary>
        /// Handler for Adding new Group
        /// </summary>
        private void AddNewGroupCommandExecute()
        {
            // Save the current group.
            SaveStateHandler saveState = this.EnforceSaveForChanges();
            if (saveState.SaveOptionSelected == SaveStatus.Valid)
            {
                GroupViewModel baseGroup = null;
                if (this.selectedModel.Parent != null)
                {
                    baseGroup = GroupViewModel.CreateClone((GroupViewModel)this.selectedModel.Parent);
                }
                else
                {
                    baseGroup = GroupViewModel.CreateClone((GroupViewModel)this.selectedModel);
                }

                this.SelectedModel = new GroupViewModel();
                this.EditableGroupModel.Parent = baseGroup;
                this.EditableGroupModel.NonVirtualGroup = true;
            }
        }

        /// <summary>
        /// Handler for Delete Workflow command
        /// </summary>
        private void DeleteGroupCommandExecute()
        {
            try
            {
                // Check whether the registry is having any active group having administrator role  
                if (SR.Group.CommonSearches.GetAllAdminGroupsCount(this.registryManagerService.RegistryConnection, this.EditableGroupModel.Id) == 0)
                {
                    throw new TridentCustomException(ManagementStudioResourceManager.GetString("ErrorIFNOAdminGroupIsActive"));
                }

                if (!this.IsGroupDeleted())
                {
                    MessageBoxResult res = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(ManagementStudioResourceManager.GetString("DeleteGroupConfirmation"));
                    if (res == MessageBoxResult.Yes)
                    {
                        try
                        {
                            this.registryManagerService.DeleteGroup(this.editableGroupModel.Id);
                            this.editableGroupModel.IsDeleted = true;
                            if (this.DeleteCommand != null)
                            {
                                this.DeleteCommand.Execute(this.SelectedModel);
                            }
                        }
                        catch (SR.RecordNotFoundException)
                        {
                            throw new TridentCustomException(TridentResourceManager.GetString("GroupDoesNotExist"));
                        }
                    }
                }
                else
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("GroupAlreadyDeleted"));
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(ex);
            }
        }

        /// <summary>
        /// Handler for Save Command
        /// </summary>
        /// <param name="parameters"></param>
        private void SaveUserChangesExecute(object[] parameters)
        {
            this.saveInProgress = true;
            bool saveSucceeded = true;
            SR.Group savedGroup = null;

            try
            {
                if (!this.IsGroupDeleted())
                {
                    saveSucceeded = ValidateSave(saveSucceeded);

                    if (saveSucceeded)
                    {
                        this.editableGroupModel.Name = this.editableGroupModel.Name.Trim();
                        saveSucceeded = this.registryManagerService.SaveGroup(this.EditableGroupModel, out savedGroup);
                    }
                    else
                    {
                        saveSucceeded = false;
                        TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("GroupDataEnteredInvalid"));
                    }
                }
                else
                {
                    saveSucceeded = false;
                    TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("GroupAlreadyDeleted"));
                }
            }
            catch (TridentCustomException ex)
            {
                if (ex.ErrorNumber.Equals(TridentErrorConstants.ErrorNumber1000000076.ToString(CultureInfo.CurrentCulture)))
                {
                    TridentMessageBox.ShowTridentWarningMessageBox(ex.Message);
                    TridentErrorHandler.HandleUIException(ex);
                    saveSucceeded = true;
                }
                else
                {
                    TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
                    saveSucceeded = false;
                }
            }
            catch (Exception ex)
            {
                saveSucceeded = false;
                TridentErrorHandler.HandleKnownExceptionsInUI(ex);
            }

            if (saveSucceeded)
            {
                if (savedGroup != null)
                {
                    this.EditableGroupModel.Update(savedGroup);
                }

                this.EditableGroupModel.IsSelected = true;
                this.DeleteGroupCommand.RaiseCanExecuteChanged();

                if (this.UpdateCommand != null)
                {
                    this.UpdateCommand.Execute(this.EditableGroupModel);
                    this.EditableGroupModel = GroupViewModel.CreateClone(this.SelectedModel as GroupViewModel);
                }

                this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("SaveSuccessGroup"));
            }

            if (parameters != null && parameters.Length > 1)
            {
                SaveStateHandler saveSuccess = parameters[1] as SaveStateHandler;
                saveSuccess.SaveOptionSelected = saveSucceeded ? SaveStatus.Valid : SaveStatus.Invalid;
                if (!saveSucceeded && !this.SelectedModel.IsSelected)
                {
                    GroupViewModel backUpModel = GroupViewModel.CreateClone(this.EditableGroupModel);
                    this.Rollback();
                    this.SelectedModel.IsSelected = true;
                    this.EditableGroupModel = GroupViewModel.CreateClone(backUpModel);
                }
            }

            this.saveInProgress = false;
        }

        /// <summary>
        /// Checks the model is valid for save
        /// </summary>
        /// <param name="saveSucceeded">saveSucceeded</param>
        /// <returns>true if model is valid false other wise</returns>
        private bool ValidateSave(bool saveSucceeded)
        {
            if (string.IsNullOrEmpty(this.editableGroupModel.Name))
            {
                saveSucceeded = false;
            }
            else if (string.IsNullOrEmpty(this.editableGroupModel.Name.Trim()))
            {
                saveSucceeded = false;
            }
            else if (!TridentWorkflowModel.CheckNameForInvalidCharacters(this.editableGroupModel.Name.Trim()))
            {
                saveSucceeded = false;
            }


            CredentialRoleModel roleModel = this.editableGroupModel.Roles.FirstOrDefault(p => p.RoleSelected == true);

            if (roleModel == null)
            {
                saveSucceeded = false;
            }

            return saveSucceeded;
        }

        /// <summary>
        /// Checks wether the Group has been deleted by another User or not
        /// If the Group has been deleted It will throw Custom Exception
        /// </summary>
        /// <returns>false if its not deleted</returns>
        private bool IsGroupDeleted()
        {
            bool groupDeleted = false;
            if (!this.EditableGroupModel.Id.Equals(Guid.Empty))
            {
                SR.Group deleteGroup = SR.Group.Load(this.EditableGroupModel.Id, this.registryManagerService.RegistryConnection);
                if (deleteGroup != null && deleteGroup.IsDeleted)
                {
                    groupDeleted = true;
                    this.EditableGroupModel.IsDeleted = true;
                    if (this.DeleteCommand != null)
                    {
                        this.DeleteCommand.Execute(this.SelectedModel);
                    }
                }
            }

            return groupDeleted;
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Called by a property when it wants to notify that its value has changed.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}